/*
 * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
namespace IMLibrary.Audio.iLBC
{

    /**
     * @author Jean Lorchat
     */
    public class ilbc_decoder {

        int consPLICount;
        int prevPLI;
        int prevLag;
        int last_lag;
        int prev_enh_pl;
        float per;
        float[] prevResidual;
        long seed;
        float[] prevLpc;

        ilbc_ulp ULP_inst = null;

        float[] syntMem;
        float[] lsfdeqold;
        float[] old_syntdenum;
        float[] hpomem;
        int use_enhancer;
        float[] enh_buf;
        float[] enh_period;

        // La plupart des variables globales sont dans ilbc_constants.etc...


        void syntFilter(
            float[] Out,     /* (i/o) Signal to be filtered */
            int Out_idx,
            float[] a,       /* (i) LP parameters */
            int a_idx,
            int len,    /* (i) Length of signal */
            float[] mem)      /* (i/o) Filter state */
         {
            int i, j;
            //    float *po, *pi, *pa, *pm;
            int po, pi, pa, pm;

            //     System.out.println("out size : " + Out.Length);
            //     System.out.println("out idx : " + Out_idx);
            //     System.out.println("a size : " + a.Length);
            //     System.out.println("a idx : " + a_idx);
            //     System.out.println("len : " + len);
            //     System.out.println("mem size : " + mem.Length);

            po = Out_idx;

            /* Filter first part using memory from past */

            for (i = 0;i < ilbc_constants.LPC_FILTERORDER;i++) {
                //            pi=&Out[i-1];
                //            pa=&a[1];
                //            pm=&mem[LPC_FILTERORDER-1];
                pi = Out_idx + i - 1;
                pa = a_idx + 1;
                pm = ilbc_constants.LPC_FILTERORDER - 1;

                for (j = 1;j <= i;j++) {
                    //        *po-=(*pa++)*(*pi--);
                    //         System.out.println("1 Soustraction (" + i + "," + j + ") a " + Out[po] + " de " + a[pa] + " * " + Out[pi]);
                    //         System.out.println("index " + (po - Out_idx) + " <> " + (pi - Out_idx));
                    Out[po] -= a[pa] * Out[pi];
                    //         System.out.println("Pour un resultat de " + Out[po]);
                    pa++;
                    pi--;
                }
                for (j = i + 1;j < ilbc_constants.LPC_FILTERORDER + 1;j++) {
                    //               *po-=(*pa++)*(*pm--);
                    //            System.out.println("2 Soustraction a " + Out[po] + " de " + a[pa] + " * " + mem[pm]);
                    Out[po] -= a[pa] * mem[pm];
                    //            System.out.println("Pour un resultat de " + Out[po]);
                    pa++;
                    pm--;
                }
                po++;
            }

            /* Filter last part where the state is entirely in
               the output vector */

            for (i = ilbc_constants.LPC_FILTERORDER;i < len;i++) {
                //           pi=&Out[i-1];
                pi = Out_idx + i - 1;
                //           pa=&a[1];
                pa = a_idx + 1;
                for (j = 1;j < ilbc_constants.LPC_FILTERORDER + 1;j++) {
                    //               *po-=(*pa++)*(*pi--);
                    //            System.out.println("3 Soustraction a " + Out[po] + " de " + a[pa] + " * " + Out[pi]);
                    Out[po] -= a[pa] * Out[pi];
                    //            System.out.println("Pour un resultat de " + Out[po]);
                    pa++;
                    pi--;
                }
                po++;
            }

            /* Update state vector */

            System.Array.Copy(Out, Out_idx + len - ilbc_constants.LPC_FILTERORDER,
                 mem, 0, ilbc_constants.LPC_FILTERORDER);
            //        memcpy(mem, &Out[len-LPC_FILTERORDER],
            //            LPC_FILTERORDER*sizeof(float));
        }

        /*---------------------------------------------------------------*
         *  interpolation of lsf coefficients for the decoder
         *--------------------------------------------------------------*/

        public void LSFinterpolate2a_dec(
           float[] a,           /* (o) lpc coefficients for a sub-frame */
           float[] lsf1,    /* (i) first lsf coefficient vector */
           float[] lsf2,    /* (i) second lsf coefficient vector */
           int lsf2_idx,
           float coef,         /* (i) interpolation weight */
           int length          /* (i) length of lsf vectors */
       ) {
            float[] lsftmp = new float[ilbc_constants.LPC_FILTERORDER];

            ilbc_common.interpolate(lsftmp, lsf1, lsf2, lsf2_idx, coef, length);
            ilbc_common.lsf2a(a, lsftmp);
        }

        /*---------------------------------------------------------------*
         *  obtain dequantized lsf coefficients from quantization index
         *--------------------------------------------------------------*/

        void SimplelsfDEQ(
            float[] lsfdeq,    /* (o) dequantized lsf coefficients */
            int[] index,         /* (i) quantization index */
            int lpc_n           /* (i) number of LPCs */
        ) {
            int i, j, pos, cb_pos;

            /* decode first LSF */

            pos = 0;
            cb_pos = 0;
            for (i = 0;i < ilbc_constants.LSF_NSPLIT;i++) {
                for (j = 0;j < ilbc_constants.dim_lsfCbTbl[i];j++) {
                    lsfdeq[pos + j] = ilbc_constants.lsfCbTbl[cb_pos + (int)
                        ((long) (index[i]) * ilbc_constants.dim_lsfCbTbl[i] + j)];
                }
                pos += ilbc_constants.dim_lsfCbTbl[i];
                cb_pos += ilbc_constants.size_lsfCbTbl[i] * ilbc_constants.dim_lsfCbTbl[i];
            }

            if (lpc_n > 1) {

                /* decode last LSF */

                pos = 0;
                cb_pos = 0;
                for (i = 0;i < ilbc_constants.LSF_NSPLIT;i++) {
                    for (j = 0;j < ilbc_constants.dim_lsfCbTbl[i];j++) {
                        lsfdeq[ilbc_constants.LPC_FILTERORDER + pos + j] =
                            ilbc_constants.lsfCbTbl[cb_pos + (int)
                            ((long) (index[ilbc_constants.LSF_NSPLIT + i]) *
                            ilbc_constants.dim_lsfCbTbl[i]) + j];
                    }
                    pos += ilbc_constants.dim_lsfCbTbl[i];
                    cb_pos += ilbc_constants.size_lsfCbTbl[i] * ilbc_constants.dim_lsfCbTbl[i];
                }
            }
        }

        /*----------------------------------------------------------------*
         *  obtain synthesis and weighting filters form lsf coefficients
         *---------------------------------------------------------------*/

        void DecoderInterpolateLSF(
            float[] syntdenum, /* (o) synthesis filter coefficients */
            float[] weightdenum, /* (o) weighting denumerator
                                  coefficients */
            float[] lsfdeq,       /* (i) dequantized lsf coefficients */
            int length)         /* (i) length of lsf coefficient vector */
         {
            int i, pos, lp_length;
            float[] lp = new float[ilbc_constants.LPC_FILTERORDER + 1];
            int lsfdeq2;

            lsfdeq2 = length;
            //        lsfdeq2 = lsfdeq + length;
            lp_length = length + 1;

            if (this.ULP_inst.mode == 30) {
                /* sub-frame 1: Interpolation between old and first */

                LSFinterpolate2a_dec(lp, this.lsfdeqold, lsfdeq, 0,
                     ilbc_constants.lsf_weightTbl_30ms[0], length);
                System.Array.Copy(lp, 0, syntdenum, 0, lp_length);
                //            memcpy(syntdenum,lp,lp_length*sizeof(float));
                ilbc_common.bwexpand(weightdenum, 0, lp, ilbc_constants.LPC_CHIRP_WEIGHTDENUM, lp_length);

                /* sub-frames 2 to 6: interpolation between first
                   and last LSF */

                pos = lp_length;
                for (i = 1;i < 6;i++) {
                    LSFinterpolate2a_dec(lp, lsfdeq, lsfdeq, lsfdeq2,
                         ilbc_constants.lsf_weightTbl_30ms[i], length);
                    System.Array.Copy(lp, 0, syntdenum, pos, lp_length);
                    //                memcpy(syntdenum + pos,lp,lp_length*sizeof(float));
                    ilbc_common.bwexpand(weightdenum, pos, lp,
                 ilbc_constants.LPC_CHIRP_WEIGHTDENUM, lp_length);
                    pos += lp_length;
                }
            } else {
                pos = 0;
                for (i = 0;i < this.ULP_inst.nsub;i++) {
                    LSFinterpolate2a_dec(lp, this.lsfdeqold,
                         lsfdeq, 0, ilbc_constants.lsf_weightTbl_20ms[i], length);
                    System.Array.Copy(lp, 0, syntdenum, pos, lp_length);
                    //                memcpy(syntdenum+pos,lp,lp_length*sizeof(float));
                    ilbc_common.bwexpand(weightdenum, pos, lp, ilbc_constants.LPC_CHIRP_WEIGHTDENUM,
                         lp_length);
                    pos += lp_length;
                }
            }

            /* update memory */

            if (this.ULP_inst.mode == 30) {
                System.Array.Copy(lsfdeq, lsfdeq2, this.lsfdeqold, 0, length);
                //            memcpy(iLBCdec_inst->lsfdeqold, lsfdeq2, length*sizeof(float));
            } else {
                System.Array.Copy(lsfdeq, 0, this.lsfdeqold, 0, length);
                //            memcpy(iLBCdec_inst->lsfdeqold, lsfdeq, length*sizeof(float));
            }
        }


        public void index_conv_dec(int[] index)          /* (i/o) Codebook indexes */
        {
            int k;

            for (k = 1;k < ilbc_constants.CB_NSTAGES;k++) {

                if ((index[k] >= 44) && (index[k] < 108)) {
                    index[k] += 64;
                } else if ((index[k] >= 108) && (index[k] < 128)) {
                    index[k] += 128;
                } else {
                    /* ERROR */
                }
            }
        }

        public void hpOutput(
             float[] In,  /* (i) vector to filter */
             int len,/* (i) length of vector to filter */
             float[] Out, /* (o) the resulting filtered vector */
             float[] mem)  /* (i/o) the filter state */
        {
            int i;
            //    float *pi, *po;
            int pi, po;

            /* all-zero section*/

            //        pi = &In[0];
            //        po = &Out[0];
            pi = 0;
            po = 0;

            for (i = 0;i < len;i++) {
                Out[po] = ilbc_constants.hpo_zero_coefsTbl[0] * (In[pi]);
                Out[po] += ilbc_constants.hpo_zero_coefsTbl[1] * mem[0];
                Out[po] += ilbc_constants.hpo_zero_coefsTbl[2] * mem[1];

                mem[1] = mem[0];
                mem[0] = In[pi];
                po++;
                pi++;

            }

            /* all-pole section*/

            //       po = &Out[0];
            po = 0;
            for (i = 0;i < len;i++) {
                Out[po] -= ilbc_constants.hpo_pole_coefsTbl[1] * mem[2];
                Out[po] -= ilbc_constants.hpo_pole_coefsTbl[2] * mem[3];

                mem[3] = mem[2];
                mem[2] = Out[po];
                po++;
            }
        }

        /*----------------------------------------------------------------*
         * downsample (LP filter and decimation)
         *---------------------------------------------------------------*/

        void DownSample(
            float[] In,     /* (i) input samples */
            int in_idx,
            float[] Coef,   /* (i) filter coefficients */
            int lengthIn,   /* (i) number of input samples */
            float[] state,  /* (i) filter state */
            float[] Out)     /* (o) downsampled output */
         {
            float o;
            //    float *Out_ptr = Out;
            int out_ptr = 0;
            //float *Coef_ptr, *In_ptr;
            int coef_ptr = 0;
            int in_ptr = in_idx;
            //float *state_ptr;
            int state_ptr = 0;
            int i, j, stop;

            /* LP filter and decimate at the same time */

            for (i = ilbc_constants.DELAY_DS;i < lengthIn;i += ilbc_constants.FACTOR_DS) {
                coef_ptr = 0;
                in_ptr = in_idx + i;
                state_ptr = ilbc_constants.FILTERORDER_DS - 2;

                o = 0.0f;

                //        stop = (i < ilbc_constants.FILTERORDER_DS) ? i + 1 : ilbc_constants.FILTERORDER_DS;
                if (i < ilbc_constants.FILTERORDER_DS) {
                    stop = i + 1;
                } else {
                    stop = ilbc_constants.FILTERORDER_DS;
                }

                for (j = 0;j < stop;j++) {
                    o += Coef[coef_ptr] * In[in_ptr];
                    coef_ptr++;
                    in_ptr--;
                }
                for (j = i + 1;j < ilbc_constants.FILTERORDER_DS;j++) {
                    o += Coef[coef_ptr] * state[state_ptr];
                    coef_ptr++;
                    state_ptr--;
                }
                Out[out_ptr] = o;
                out_ptr++;
                //        *Out_ptr++ = o;
            }

            /* Get the last part (use zeros as input for the future) */

            for (i = (lengthIn + ilbc_constants.FACTOR_DS);i < (lengthIn + ilbc_constants.DELAY_DS);
                    i += ilbc_constants.FACTOR_DS) {

                o = 0.0f;

                if (i < lengthIn) {
                    coef_ptr = 0;
                    in_ptr = in_idx + i;
                    for (j = 0;j < ilbc_constants.FILTERORDER_DS;j++) {
                        o += Coef[coef_ptr] * Out[out_ptr];
                        coef_ptr++;
                        out_ptr--;
                        //                       o += *Coef_ptr++ * (*Out_ptr--);
                    }
                } else {
                    coef_ptr = i - lengthIn;
                    in_ptr = in_idx + lengthIn - 1;
                    for (j = 0;j < ilbc_constants.FILTERORDER_DS - (i - lengthIn);j++) {
                        o += Coef[coef_ptr] * In[in_ptr];
                        coef_ptr++;
                        in_ptr--;
                    }
                }
                Out[out_ptr] = o;
                out_ptr++;
            }
        }

        /*----------------------------------------------------------------*
         * Find index in array such that the array element with said
         * index is the element of said array closest to "value"
         * according to the squared-error criterion
         *---------------------------------------------------------------*/

        public int NearestNeighbor(
            //             int   index[],   /* (o) index of array element closest
            //                        to value */
                       float[] array,   /* (i) data array */
                       float value,/* (i) value */
                       int arlength)/* (i) dimension of data array */
        {
            int i;
            float bestcrit, crit;
            int index;

            crit = array[0] - value;
            bestcrit = crit * crit;
            index = 0;
            for (i = 1;i < arlength;i++) {
                crit = array[i] - value;
                crit = crit * crit;

                if (crit < bestcrit) {
                    bestcrit = crit;
                    index = i;
                }
            }
            return index;
        }

        /*----------------------------------------------------------------*
         * compute cross correlation between sequences
         *---------------------------------------------------------------*/

        public void mycorr1(
             float[] corr,    /* (o) correlation of seq1 and seq2 */
             int corr_idx,
             float[] seq1,    /* (i) first sequence */
             int seq1_idx,
             int dim1,           /* (i) dimension first seq1 */
             float[] seq2,  /* (i) second sequence */
             int seq2_idx,
             int dim2)        /* (i) dimension seq2 */
        {
            int i, j;

            //     System.out.println("longueur 1 : " + seq1.Length);
            //     System.out.println("distance 1 : " + seq1_idx);
            //     System.out.println("longueur 2 : " + seq2.Length);
            //     System.out.println("distance 2 : " + seq2_idx);

            //     System.out.println("dimensions : " + dim1 + " et " + dim2);

            // BUG in ILBC ???

            for (i = 0;i <= dim1 - dim2;i++) {
                if ((corr_idx + i) < corr.Length)
                    corr[corr_idx + i] = 0.0f;
                for (j = 0;j < dim2;j++) {
                    corr[corr_idx + i] += seq1[seq1_idx + i + j] * seq2[seq2_idx + j];
                }
            }
        }

        /*----------------------------------------------------------------*
         * upsample finite array assuming zeros outside bounds
         *---------------------------------------------------------------*/

        public void enh_upsample(
                  float[] useq1,   /* (o) upsampled output sequence */
                  float[] seq1,/* (i) unupsampled sequence */
                  int dim1,       /* (i) dimension seq1 */
                  int hfl)         /* (i) polyphase filter length=2*hfl+1 */
        {
            //    float *pu,*ps;
            int pu, ps;
            int i, j, k, q, filterlength, hfl2;
            int[] polyp = new int[ilbc_constants.ENH_UPS0]; /* pointers to
                     polyphase columns */
            //    const float *pp;
            int pp;

            /* define pointers for filter */

            filterlength = 2 * hfl + 1;

            if (filterlength > dim1) {
                hfl2 = (dim1 / 2);
                for (j = 0;j < ilbc_constants.ENH_UPS0;j++) {
                    polyp[j] = j * filterlength + hfl - hfl2;
                }
                hfl = hfl2;
                filterlength = 2 * hfl + 1;
            } else {
                for (j = 0;j < ilbc_constants.ENH_UPS0;j++) {
                    polyp[j] = j * filterlength;
                }
            }

            /* filtering: filter overhangs left side of sequence */

            //    pu=useq1;
            pu = 0;
            for (i = hfl;i < filterlength;i++) {
                for (j = 0;j < ilbc_constants.ENH_UPS0;j++) {
                    //        *pu=0.0f;
                    useq1[pu] = 0.0f;
                    //        pp = polyp[j];
                    pp = polyp[j];
                    //        ps = seq1+i;
                    ps = i;
                    for (k = 0;k <= i;k++) {
                        useq1[pu] += seq1[ps] * ilbc_constants.polyphaserTbl[pp];
                        ps--;
                        pp++;
                    }
                    pu++;
                }
            }

            /* filtering: simple convolution=inner products */

            for (i = filterlength;i < dim1;i++) {
                for (j = 0;j < ilbc_constants.ENH_UPS0;j++) {
                    //            *pu=0.0f;
                    useq1[pu] = 0.0f;
                    //            pp = polyp[j];
                    pp = polyp[j];
                    //            ps = seq1+i;
                    ps = i;
                    for (k = 0;k < filterlength;k++) {
                        //            *pu += *ps-- * *pp++;
                        useq1[pu] += seq1[ps] * ilbc_constants.polyphaserTbl[pp];
                        ps--;
                        pp++;
                    }
                    pu++;
                }
            }

            /* filtering: filter overhangs right side of sequence */

            for (q = 1;q <= hfl;q++) {
                for (j = 0;j < ilbc_constants.ENH_UPS0;j++) {
                    //        *pu=0.0f;
                    useq1[pu] = 0.0f;
                    //        pp = polyp[j]+q;
                    pp = polyp[j] + q;
                    //        ps = seq1+dim1-1;
                    ps = dim1 - 1;
                    for (k = 0;k < filterlength - q;k++) {
                        useq1[pu] += seq1[ps] * ilbc_constants.polyphaserTbl[pp];
                        ps--;
                        pp++;
                        //            *pu += *ps-- * *pp++;
                    }
                    pu++;
                }
            }
        }


        /**
         * @param period Currently not used
         */
        /*----------------------------------------------------------------*
         * find segment starting near idata+estSegPos that has highest
         * correlation with idata+centerStartPos through
         * idata+centerStartPos+ENH_BLOCKL-1 segment is found at a
         * resolution of ENH_UPSO times the original of the original
         * sampling rate
         *---------------------------------------------------------------*/

        public float refiner(
             float[] seg,         /* (o) segment array */
             int seg_idx,
             float[] idata,       /* (i) original data buffer */
             int idatal,         /* (i) dimension of idata */
             int centerStartPos, /* (i) beginning center segment */
             float estSegPos,/* (i) estimated beginning other segment */
             float period)    /* (i) estimated pitch period */
        {
            int estSegPosRounded, searchSegStartPos, searchSegEndPos, corrdim;
            int tloc, tloc2, i, st, en, fraction;
            float[] vect = new float[ilbc_constants.ENH_VECTL];
            float[] corrVec = new float[ilbc_constants.ENH_CORRDIM];
            float maxv;
            float[] corrVecUps = new float[ilbc_constants.ENH_CORRDIM * ilbc_constants.ENH_UPS0];
            float updStartPos = 0.0f;

            /* defining array bounds */

            estSegPosRounded = (int) (estSegPos - 0.5);

            searchSegStartPos = estSegPosRounded - ilbc_constants.ENH_SLOP;

            if (searchSegStartPos < 0) {
                searchSegStartPos = 0;
            }
            searchSegEndPos = estSegPosRounded + ilbc_constants.ENH_SLOP;

            if (searchSegEndPos + ilbc_constants.ENH_BLOCKL >= idatal) {
                searchSegEndPos = idatal - ilbc_constants.ENH_BLOCKL - 1;
            }
            corrdim = searchSegEndPos - searchSegStartPos + 1;

            /* compute upsampled correlation (corr33) and find
               location of max */
            //     System.out.println("appel 1");
            mycorr1(corrVec, 0, idata, searchSegStartPos,
                corrdim + ilbc_constants.ENH_BLOCKL - 1,
                idata, centerStartPos, ilbc_constants.ENH_BLOCKL);
            enh_upsample(corrVecUps, corrVec, corrdim, ilbc_constants.ENH_FL0);
            tloc = 0; maxv = corrVecUps[0];
            for (i = 1;i < ilbc_constants.ENH_UPS0 * corrdim;i++) {

                if (corrVecUps[i] > maxv) {
                    tloc = i;
                    maxv = corrVecUps[i];
                }
            }

            /* make vector can be upsampled without ever running outside
               bounds */

            updStartPos = searchSegStartPos +
                (float) tloc / (float) ilbc_constants.ENH_UPS0 + 1.0f;
            tloc2 = tloc / ilbc_constants.ENH_UPS0;

            if (tloc > tloc2 * ilbc_constants.ENH_UPS0) {
                tloc2++;
            }
            st = searchSegStartPos + tloc2 - ilbc_constants.ENH_FL0;

            if (st < 0) {
                for (int li = 0;li < -st;li++)
                    vect[li] = 0.0f;
                //         memset(vect,0,-st*sizeof(float));
                System.Array.Copy(idata, 0, vect, -st, (ilbc_constants.ENH_VECTL + st));
                //         memcpy(&vect[-st],idata, (ilbc_constants.ENH_VECTL+st)*sizeof(float));
            } else {
                en = st + ilbc_constants.ENH_VECTL;

                if (en > idatal) {
                    System.Array.Copy(idata, st, vect, 0, (ilbc_constants.ENH_VECTL - (en - idatal)));
                    //         memcpy(vect, &idata[st],
                    //                (ilbc_constants.ENH_VECTL-(en-idatal))*sizeof(float));
                    for (int li = 0;li < en - idatal;li++)
                        vect[ilbc_constants.ENH_VECTL - (en - idatal) + li] = 0.0f;
                    //         memset(&vect[ilbc_constants.ENH_VECTL-(en-idatal)], 0,
                    //                (en-idatal)*sizeof(float));
                } else {
                    System.Array.Copy(idata, st, vect, 0, ilbc_constants.ENH_VECTL);
                    //         memcpy(vect, &idata[st], ilbc_constants.ENH_VECTL*sizeof(float));
                }
            }
            fraction = tloc2 * ilbc_constants.ENH_UPS0 - tloc;

            /* compute the segment (this is actually a convolution) */

            //     System.out.println("appel 2");
            //     System.out.println("longueur 1 : " + vect.Length);
            //     System.out.println("distance 1 : " + 0);
            //     System.out.println("longueur 2 : " + ilbc_constants.polyphaserTbl.Length);
            //     System.out.println("distance 2 : " + (2*ilbc_constants.ENH_FL0+1)*fraction);
            //     System.out.println("dimension 1 : " + ilbc_constants.ENH_VECTL);
            //     System.out.println("dimension 2 : " + (2 * ilbc_constants.ENH_FL0+1));
            //     System.out.println("correlations de dimension " + seg.Length);
            mycorr1(seg, seg_idx, vect, 0, ilbc_constants.ENH_VECTL,
                ilbc_constants.polyphaserTbl,
                (2 * ilbc_constants.ENH_FL0 + 1) * fraction,
                2 * ilbc_constants.ENH_FL0 + 1);

            return updStartPos;
        }

        /*----------------------------------------------------------------*
         * find the smoothed output data
         *---------------------------------------------------------------*/

        public void smath(
               float[] odata,   /* (o) smoothed output */
               int odata_idx,
               float[] sseq,/* (i) said second sequence of waveforms */
               int hl,         /* (i) 2*hl+1 is sseq dimension */
               float alpha0)/* (i) max smoothing energy fraction */
        {
            int i, k;
            float w00, w10, w11, A, B, C, err, errs;
            float[] surround = new float[ilbc_constants.BLOCKL_MAX]; /* shape contributed by other than
                       current */
            float[] wt = new float[2 * ilbc_constants.ENH_HL + 1];       /* waveform weighting to get
                       surround shape */
            float denom;
            int psseq;

            /* create shape of contribution from all waveforms except the
               current one */

            for (i = 1;i <= 2 * hl + 1;i++) {
                wt[i - 1] = (float) 0.5 * (1 - (float) System.Math.Cos(2 * ilbc_constants.PI * i / (2 * hl + 2)));
            }
            wt[hl] = 0.0f; /* for clarity, not used */
            for (i = 0;i < ilbc_constants.ENH_BLOCKL;i++) {
                surround[i] = sseq[i] * wt[0];
            }
            for (k = 1;k < hl;k++) {
                psseq = k * ilbc_constants.ENH_BLOCKL;
                for (i = 0;i < ilbc_constants.ENH_BLOCKL;i++) {
                    surround[i] += sseq[psseq + i] * wt[k];
                }
            }
            for (k = hl + 1;k <= 2 * hl;k++) {
                psseq = k * ilbc_constants.ENH_BLOCKL;
                for (i = 0;i < ilbc_constants.ENH_BLOCKL;i++) {
                    surround[i] += sseq[psseq + i] * wt[k];
                }
            }

            /* compute some inner products */

            w00 = w10 = w11 = 0.0f;
            psseq = hl * ilbc_constants.ENH_BLOCKL; /* current block  */
            for (i = 0;i < ilbc_constants.ENH_BLOCKL;i++) {
                w00 += sseq[psseq + i] * sseq[psseq + i];
                w11 += surround[i] * surround[i];
                w10 += surround[i] * sseq[psseq + i];
            }

            if (System.Math.Abs(w11) < 1.0f) {
                w11 = 1.0f;
            }
            C = (float) System.Math.Sqrt(w00 / w11);

            /* first try enhancement without power-constraint */

            errs = 0.0f;
            psseq = hl * ilbc_constants.ENH_BLOCKL;
            for (i = 0;i < ilbc_constants.ENH_BLOCKL;i++) {
                odata[odata_idx + i] = C * surround[i];
                err = sseq[psseq + i] - odata[odata_idx + i];
                errs += err * err;
            }

            /* if constraint violated by first try, add constraint */

            if (errs > alpha0 * w00) {
                if (w00 < 1) {
                    w00 = 1;
                }
                denom = (w11 * w00 - w10 * w10) / (w00 * w00);

                if (denom > 0.0001f) { /* eliminates numerical problems
                     for if smooth */
                    A = (float) System.Math.Sqrt((alpha0 - alpha0 * alpha0 / 4) / denom);
                    B = -alpha0 / 2 - A * w10 / w00;
                    B = B + 1;
                } else { /* essentially no difference between cycles;
              smoothing not needed */
                    A = 0.0f;
                    B = 1.0f;
                }

                /* create smoothed sequence */

                psseq = hl * ilbc_constants.ENH_BLOCKL;
                for (i = 0;i < ilbc_constants.ENH_BLOCKL;i++) {
                    odata[odata_idx + i] = A * surround[i] + B * sseq[psseq + i];
                }
            }
        }

        /*----------------------------------------------------------------*
         * get the pitch-synchronous sample sequence
         *---------------------------------------------------------------*/

        public void getsseq(
             float[] sseq,    /* (o) the pitch-synchronous sequence */
             float[] idata,       /* (i) original data */
             int idatal,         /* (i) dimension of data */
             int centerStartPos, /* (i) where current block starts */
             float[] period,      /* (i) rough-pitch-period array */
             float[] plocs,       /* (i) where periods of period array
                    are taken */
             int periodl,    /* (i) dimension period array */
             int hl)              /* (i) 2*hl+1 is the number of sequences */
        {
            int i, centerEndPos, q;
            float[] blockStartPos = new float[2 * ilbc_constants.ENH_HL + 1];
            int[] lagBlock = new int[2 * ilbc_constants.ENH_HL + 1];
            float[] plocs2 = new float[ilbc_constants.ENH_PLOCSL];
            //    float *psseq;
            int psseq;

            centerEndPos = centerStartPos + ilbc_constants.ENH_BLOCKL - 1;

            /* present */

            lagBlock[hl] = NearestNeighbor(plocs,
                    (float) 0.5 * (centerStartPos + centerEndPos), periodl);

            blockStartPos[hl] = centerStartPos;

            psseq = ilbc_constants.ENH_BLOCKL * hl;
            //     psseq=sseq+ENH_BLOCKL*hl;
            System.Array.Copy(idata, centerStartPos, sseq, psseq, ilbc_constants.ENH_BLOCKL);
            //    memcpy(psseq, idata+centerStartPos, ENH_BLOCKL*sizeof(float));

            /* past */

            for (q = hl - 1;q >= 0;q--) {
                blockStartPos[q] = blockStartPos[q + 1] - period[lagBlock[q + 1]];
                lagBlock[q] = NearestNeighbor(plocs,
                              blockStartPos[q] +
                              ilbc_constants.ENH_BLOCKL_HALF - period[lagBlock[q + 1]],
                              periodl);


                if (blockStartPos[q] - ilbc_constants.ENH_OVERHANG >= 0) {
                    blockStartPos[q] = refiner(sseq, q * ilbc_constants.ENH_BLOCKL, idata,
                                   idatal, centerStartPos,
                                   blockStartPos[q],
                                   period[lagBlock[q + 1]]);
                } else {
                    psseq = q * ilbc_constants.ENH_BLOCKL;
                    //         psseq=sseq+q*ENH_BLOCKL;
                    for (int li = 0;li < ilbc_constants.ENH_BLOCKL;li++)
                        sseq[psseq + li] = 0.0f;
                    //        memset(psseq, 0, ENH_BLOCKL*sizeof(float));
                }
            }

            /* future */

            for (i = 0;i < periodl;i++) {
                plocs2[i] = plocs[i] - period[i];
            }
            for (q = hl + 1;q <= 2 * hl;q++) {
                lagBlock[q] = NearestNeighbor(plocs2,
                              blockStartPos[q - 1] + ilbc_constants.ENH_BLOCKL_HALF,
                              periodl);

                blockStartPos[q] = blockStartPos[q - 1] + period[lagBlock[q]];
                if (blockStartPos[q] + ilbc_constants.ENH_BLOCKL + ilbc_constants.ENH_OVERHANG < idatal) {
                    blockStartPos[q] = refiner(sseq, q * ilbc_constants.ENH_BLOCKL, idata,
                                   idatal, centerStartPos,
                                   blockStartPos[q],
                                   period[lagBlock[q]]);
                } else {
                    psseq = q * ilbc_constants.ENH_BLOCKL;
                    //         psseq=sseq+q*ENH_BLOCKL;
                    for (int li = 0;li < ilbc_constants.ENH_BLOCKL;li++)
                        sseq[psseq + li] = 0.0f;
                    //         memset(psseq, 0, ENH_BLOCKL*sizeof(float));
                }
            }
        }

        /*----------------------------------------------------------------*
         * perform enhancement on idata+centerStartPos through
         * idata+centerStartPos+ENH_BLOCKL-1
         *---------------------------------------------------------------*/

        public void enhancer(
                  float[] odata,       /* (o) smoothed block, dimension blockl */
                  int odata_idx,
                  float[] idata,       /* (i) data buffer used for enhancing */
                  int idatal,         /* (i) dimension idata */
                  int centerStartPos, /* (i) first sample current block
                         within idata */
                  float alpha0,       /* (i) max correction-energy-fraction
                         (in [0,1]) */
                  float[] period,      /* (i) pitch period array */
                  float[] plocs,       /* (i) locations where period array
                         values valid */
                  int periodl         /* (i) dimension of period and plocs */
                  ) {
            float[] sseq = new float[(2 * ilbc_constants.ENH_HL + 1) * ilbc_constants.ENH_BLOCKL];

            /* get said second sequence of segments */

            getsseq(sseq, idata, idatal, centerStartPos, period,
                plocs, periodl, ilbc_constants.ENH_HL);

            /* compute the smoothed output from said second sequence */

            smath(odata, odata_idx, sseq, ilbc_constants.ENH_HL, alpha0);

        }

        /*----------------------------------------------------------------*
         * cross correlation
         *---------------------------------------------------------------*/

        public float xCorrCoef(
                float[] target,      /* (i) first array */
                int t_idx,
                float[] regressor,   /* (i) second array */
                int r_idx,
                int subl)        /* (i) dimension arrays */
        {
            int i;
            float ftmp1, ftmp2;

            ftmp1 = 0.0f;
            ftmp2 = 0.0f;
            for (i = 0;i < subl;i++) {
                ftmp1 += target[t_idx + i] * regressor[r_idx + i];
                ftmp2 += regressor[r_idx + i] * regressor[r_idx + i];
            }

            if (ftmp1 > 0.0f) {
                return (ftmp1 * ftmp1 / ftmp2);
            } else {
                return 0.0f;
            }
        }

        /*----------------------------------------------------------------*
         * interface for enhancer
         *---------------------------------------------------------------*/

        int enhancerInterface(
                  float[] _out,                     /* (o) enhanced signal */
                  float[] _in)                      /* (i) unenhanced signal */
        {
            //    float *enh_buf, *enh_period; (definis en global pour la classe)
            int iblock, isample;
            int lag = 0, ilag, i, ioffset;
            float cc, maxcc;
            float ftmp1, ftmp2;
            //    float *inPtr, *enh_bufPtr1, *enh_bufPtr2;
            int inPtr, enh_bufPtr1, enh_bufPtr2;
            float[] plc_pred = new float[ilbc_constants.ENH_BLOCKL];

            float[] lpState = new float[6];
            float[] downsampled = new float[(ilbc_constants.ENH_NBLOCKS * ilbc_constants.ENH_BLOCKL + 120) / 2];
            int inLen = ilbc_constants.ENH_NBLOCKS * ilbc_constants.ENH_BLOCKL + 120;
            int start, plc_blockl, inlag;

            //    enh_buf=iLBCdec_inst->enh_buf;
            //    enh_period=iLBCdec_inst->enh_period;

            System.Array.Copy(enh_buf, this.ULP_inst.blockl,
                     enh_buf, 0,
                     ilbc_constants.ENH_BUFL - this.ULP_inst.blockl);
            //     memmove(enh_buf, &enh_buf[iLBCdec_inst->blockl],
            //         (ENH_BUFL-iLBCdec_inst->blockl)*sizeof(float));


            System.Array.Copy(_in, 0, enh_buf, ilbc_constants.ENH_BUFL - this.ULP_inst.blockl,
                     this.ULP_inst.blockl);
            //     memcpy(&enh_buf[ENH_BUFL-this.ULP_inst.blockl], in,
            //            this.ULP_inst.blockl*sizeof(float));

            if (this.ULP_inst.mode == 30)
                plc_blockl = ilbc_constants.ENH_BLOCKL;
            else
                plc_blockl = 40;

            /* when 20 ms frame, move processing one block */
            ioffset = 0;
            if (this.ULP_inst.mode == 20) ioffset = 1;

            i = 3 - ioffset;
            System.Array.Copy(enh_period, i, enh_period, 0, ilbc_constants.ENH_NBLOCKS_TOT - i);
            //     memmove(enh_period, &enh_period[i],
            //         (ENH_NBLOCKS_TOT-i)*sizeof(float));

            /* Set state information to the 6 samples right before
               the samples to be downsampled. */

            System.Array.Copy(enh_buf, (ilbc_constants.ENH_NBLOCKS_EXTRA + ioffset) * ilbc_constants.ENH_BLOCKL - 126,
                     lpState, 0, 6);
            //     memcpy(lpState,
            //            enh_buf+(ENH_NBLOCKS_EXTRA+ioffset)*ENH_BLOCKL-126,
            //            6*sizeof(float));

            /* Down sample a factor 2 to save computations */

            DownSample(enh_buf,
                   (ilbc_constants.ENH_NBLOCKS_EXTRA + ioffset) * ilbc_constants.ENH_BLOCKL - 120,
                           ilbc_constants.lpFilt_coefsTbl, inLen - ioffset * ilbc_constants.ENH_BLOCKL,
                           lpState, downsampled);

            /* Estimate the pitch in the down sampled domain. */
            for (iblock = 0;iblock < ilbc_constants.ENH_NBLOCKS - ioffset;iblock++) {

                lag = 10;
                maxcc = xCorrCoef(downsampled, 60 + iblock * ilbc_constants.ENH_BLOCKL_HALF,
                          downsampled, 60 + iblock * ilbc_constants.ENH_BLOCKL_HALF - lag,
                          ilbc_constants.ENH_BLOCKL_HALF);
                for (ilag = 11;ilag < 60;ilag++) {
                    cc = xCorrCoef(downsampled, 60 + iblock * ilbc_constants.ENH_BLOCKL_HALF,
                               downsampled, 60 + iblock * ilbc_constants.ENH_BLOCKL_HALF - ilag,
                               ilbc_constants.ENH_BLOCKL_HALF);

                    if (cc > maxcc) {
                        maxcc = cc;
                        lag = ilag;
                    }
                }

                /* Store the estimated lag in the non-downsampled domain */
                enh_period[iblock + ilbc_constants.ENH_NBLOCKS_EXTRA + ioffset] = (float) lag * 2;


            }


            /* PLC was performed on the previous packet */
            if (this.prev_enh_pl == 1) {

                inlag = (int) enh_period[ilbc_constants.ENH_NBLOCKS_EXTRA + ioffset];

                lag = inlag - 1;
                maxcc = xCorrCoef(_in, 0, _in, lag, plc_blockl);
                for (ilag = inlag;ilag <= inlag + 1;ilag++) {
                    cc = xCorrCoef(_in, 0, _in, ilag, plc_blockl);
                    if (cc > maxcc) {
                        maxcc = cc;
                        lag = ilag;
                    }
                }

                enh_period[ilbc_constants.ENH_NBLOCKS_EXTRA + ioffset - 1] = lag;

                /* compute new concealed residual for the old lookahead,
                   mix the forward PLC with a backward PLC from
                   the new frame */

                //        inPtr=&in[lag-1];
                inPtr = lag - 1;

                //        enh_bufPtr1=&plc_pred[plc_blockl-1];
                enh_bufPtr1 = plc_blockl - 1;

                if (lag > plc_blockl) {
                    start = plc_blockl;
                } else {
                    start = lag;
                }

                for (isample = start;isample > 0;isample--) {
                    //        *enh_bufPtr1-- = *inPtr--;
                    plc_pred[enh_bufPtr1] = _in[inPtr];
                    enh_bufPtr1--;
                    inPtr--;
                }

                //        enh_bufPtr2=&enh_buf[ENH_BUFL-1-this.ULP_inst.blockl];
                enh_bufPtr2 = ilbc_constants.ENH_BUFL - 1 - this.ULP_inst.blockl;
                for (isample = (plc_blockl - 1 - lag);isample >= 0;isample--) {
                    //        *enh_bufPtr1-- = *enh_bufPtr2--;
                    plc_pred[enh_bufPtr1] = enh_buf[enh_bufPtr2];
                    enh_bufPtr1--;
                    enh_bufPtr2--;
                }

                /* limit energy change */
                ftmp2 = 0.0f;
                ftmp1 = 0.0f;
                for (i = 0;i < plc_blockl;i++) {
                    ftmp2 += enh_buf[ilbc_constants.ENH_BUFL - 1 - this.ULP_inst.blockl - i] *
                        enh_buf[ilbc_constants.ENH_BUFL - 1 - this.ULP_inst.blockl - i];
                    ftmp1 += plc_pred[i] * plc_pred[i];
                }
                ftmp1 = (float) System.Math.Sqrt(ftmp1 / plc_blockl);
                ftmp2 = (float) System.Math.Sqrt(ftmp2 / plc_blockl);
                if (ftmp1 > 2.0f * ftmp2 && ftmp1 > 0.0) {
                    for (i = 0;i < plc_blockl - 10;i++) {
                        plc_pred[i] *= 2.0f * ftmp2 / ftmp1;
                    }
                    for (i = plc_blockl - 10;i < plc_blockl;i++) {
                        plc_pred[i] *= (i - plc_blockl + 10) *
                        (1.0f - (float) 2.0 * ftmp2 / ftmp1) / (10) +
                        2.0f * ftmp2 / ftmp1;
                    }
                }

                enh_bufPtr1 = ilbc_constants.ENH_BUFL - 1 - this.ULP_inst.blockl;
                //         enh_bufPtr1=&enh_buf[ilbc_constants.ENH_BUFL-1-this.ULP_inst.blockl];
                for (i = 0;i < plc_blockl;i++) {
                    ftmp1 = (float) (i + 1) / (float) (plc_blockl + 1);
                    enh_buf[enh_bufPtr1] *= ftmp1;
                    //         *enh_bufPtr1 *= ftmp1;
                    enh_buf[enh_bufPtr1] += (1.0f - ftmp1) *
                        plc_pred[plc_blockl - 1 - i];
                    //         *enh_bufPtr1 += ((float)1.0f-ftmp1)*
                    //             plc_pred[plc_blockl-1-i];
                    enh_bufPtr1--;
                }
            }

            if (this.ULP_inst.mode == 20) {
                /* Enhancer with 40 samples delay */
                for (iblock = 0;iblock < 2;iblock++) {
                    enhancer(_out, iblock * ilbc_constants.ENH_BLOCKL, enh_buf,
                         ilbc_constants.ENH_BUFL, (5 + iblock) * ilbc_constants.ENH_BLOCKL + 40,
                         ilbc_constants.ENH_ALPHA0, enh_period, ilbc_constants.enh_plocsTbl,
                         ilbc_constants.ENH_NBLOCKS_TOT);
                }
            } else if (this.ULP_inst.mode == 30) {
                /* Enhancer with 80 samples delay */
                for (iblock = 0;iblock < 3;iblock++) {
                    enhancer(_out, iblock * ilbc_constants.ENH_BLOCKL, enh_buf,
                         ilbc_constants.ENH_BUFL, (4 + iblock) * ilbc_constants.ENH_BLOCKL,
                         ilbc_constants.ENH_ALPHA0, enh_period, ilbc_constants.enh_plocsTbl,
                         ilbc_constants.ENH_NBLOCKS_TOT);
                }
            }

            return (lag * 2);
        }

        /*----------------------------------------------------------------*
         *  Packet loss concealment routine. Conceals a residual signal
         *  and LP parameters. If no packet loss, update state.
         *---------------------------------------------------------------*/

        /*----------------------------------------------------------------*
         *  Compute cross correlation and pitch gain for pitch prediction
         *  of last subframe at given lag.
         *---------------------------------------------------------------*/

        public void compCorr(
                 float[] cc,      /* (o) cross correlation coefficient */
                 float[] gc,      /* (o) gain */
                 float[] pm,
                 float[] buffer,  /* (i) signal buffer */
                 int lag,    /* (i) pitch lag */
                 int bLen,       /* (i) length of buffer */
                 int sRange)      /* (i) correlation search length */
        {
            int i;
            float ftmp1, ftmp2, ftmp3;

            /* Guard against getting outside buffer */
            if ((bLen - sRange - lag) < 0) {
                sRange = bLen - lag;
            }

            ftmp1 = 0.0f;
            ftmp2 = 0.0f;
            ftmp3 = 0.0f;

            for (i = 0;i < sRange;i++) {
                ftmp1 += buffer[bLen - sRange + i] *
                buffer[bLen - sRange + i - lag];
                ftmp2 += buffer[bLen - sRange + i - lag] *
                buffer[bLen - sRange + i - lag];
                ftmp3 += buffer[bLen - sRange + i] *
                buffer[bLen - sRange + i];
            }

            if (ftmp2 > 0.0f) {
                cc[0] = ftmp1 * ftmp1 / ftmp2;
                gc[0] = System.Math.Abs(ftmp1 / ftmp2);
                pm[0] = System.Math.Abs(ftmp1) /
                ((float) System.Math.Sqrt(ftmp2) * (float) System.Math.Sqrt(ftmp3));
            } else {
                cc[0] = 0.0f;
                gc[0] = 0.0f;
                pm[0] = 0.0f;
            }
        }

        public void doThePLC(
                 float[] PLCresidual, /* (o) concealed residual */
                 float[] PLClpc,      /* (o) concealed LP parameters */
                 int PLI,        /* (i) packet loss indicator
                        0 - no PL, 1 = PL */
                 float[] decresidual, /* (i) decoded residual */
                 float[] lpc,         /* (i) decoded LPC (only used for no PL) */
                 int lpc_idx,
                 int inlag)          /* (i) pitch lag */
        {
            int lag = 20, randlag = 0;
            float gain = 0.0f, maxcc = 0.0f;
            float use_gain = 0.0f;
            float gain_comp = 0.0f, maxcc_comp = 0.0f, per = 0.0f, max_per = 0.0f;
            int i, pick, use_lag;
            float ftmp, pitchfact, energy;
            float[] randvec;
            float[] a_gain, a_comp, a_per;

            randvec = new float[ilbc_constants.BLOCKL_MAX];

            a_gain = new float[1];
            a_comp = new float[1];
            a_per = new float[1];

            /* Packet Loss */

            if (PLI == 1) {

                this.consPLICount += 1;

                /* if previous frame not lost,
                   determine pitch pred. gain */

                if (this.prevPLI != 1) {

                    /* Search around the previous lag to find the
                       best pitch period */

                    lag = inlag - 3;

                    a_comp[0] = maxcc;
                    a_gain[0] = gain;
                    a_per[0] = max_per;
                    compCorr(a_comp, a_gain, a_per,
                         this.prevResidual,
                         lag, this.ULP_inst.blockl, 60);
                    maxcc = a_comp[0];
                    gain = a_gain[0];
                    max_per = a_per[0];

                    for (i = inlag - 2;i <= inlag + 3;i++) {

                        a_comp[0] = maxcc_comp;
                        a_gain[0] = gain_comp;
                        a_per[0] = per;
                        compCorr(a_comp, a_gain, a_per,
                             this.prevResidual,
                             i, this.ULP_inst.blockl, 60);
                        maxcc_comp = a_comp[0];
                        gain_comp = a_gain[0];
                        per = a_per[0];

                        if (maxcc_comp > maxcc) {
                            maxcc = maxcc_comp;
                            gain = gain_comp;
                            lag = i;
                            max_per = per;
                        }
                    }
                }

                /* previous frame lost, use recorded lag and periodicity */
                else {
                    lag = this.prevLag;
                    max_per = this.per;
                }

                /* downscaling */

                use_gain = 1.0f;
                if (this.consPLICount * this.ULP_inst.blockl > 320)
                    use_gain = 0.9f;
                else if (this.consPLICount * this.ULP_inst.blockl > 2 * 320)
                    use_gain = 0.7f;
                else if (this.consPLICount * this.ULP_inst.blockl > 3 * 320)
                    use_gain = 0.5f;
                else if (this.consPLICount * this.ULP_inst.blockl > 4 * 320)
                    use_gain = 0.0f;

                /* mix noise and pitch repeatition */
                ftmp = (float) System.Math.Sqrt(max_per);
                if (ftmp > 0.7f)
                    pitchfact = 1.0f;
                else if (ftmp > (float) 0.4)
                    pitchfact = (ftmp - (float) 0.4) / ((float) 0.7 - (float) 0.4);
                else
                    pitchfact = 0.0f;


                /* avoid repetition of same pitch cycle */
                use_lag = lag;
                if (lag < 80) {
                    use_lag = 2 * lag;
                }

                /* compute concealed residual */
                energy = 0.0f;
                for (i = 0;i < this.ULP_inst.blockl;i++) {

                    /* noise component */

                    this.seed = (this.seed * 69069 + 1) & (0x80000000 - 1);
                    randlag = 50 + (int) (this.seed % 70);
                    pick = i - randlag;

                    if (pick < 0) {
                        randvec[i] = this.prevResidual[this.ULP_inst.blockl + pick];
                    } else {
                        randvec[i] = randvec[pick];
                    }

                    /* pitch repeatition component */
                    pick = i - use_lag;

                    if (pick < 0) {
                        PLCresidual[i] = this.prevResidual[this.ULP_inst.blockl + pick];
                    } else {
                        PLCresidual[i] = PLCresidual[pick];
                    }

                    /* mix random and periodicity component */

                    if (i < 80)
                        PLCresidual[i] = use_gain * (pitchfact *
                                       PLCresidual[i] +
                                       (1.0f - pitchfact) * randvec[i]);
                    else if (i < 160)
                        PLCresidual[i] = (float) 0.95 * use_gain * (pitchfact *
                                            PLCresidual[i] +
                                            (1.0f - pitchfact) * randvec[i]);
                    else
                        PLCresidual[i] = (float) 0.9 * use_gain * (pitchfact *
                                           PLCresidual[i] +
                                           (1.0f - pitchfact) * randvec[i]);

                    energy += PLCresidual[i] * PLCresidual[i];
                }

                /* less than 30 dB, use only noise */

                if ((float) System.Math.Sqrt(energy / this.ULP_inst.blockl) < 30.0f) {
                    gain = 0.0f;
                    for (i = 0;i < this.ULP_inst.blockl;i++) {
                        PLCresidual[i] = randvec[i];
                    }
                }

                /* use old LPC */

                //        memcpy(PLClpc,this.prevLpc, (LPC_FILTERORDER+1)*sizeof(float));
                System.Array.Copy(this.prevLpc, 0, PLClpc, 0, ilbc_constants.LPC_FILTERORDER + 1);

            }

            /* no packet loss, copy input */
            else {
                //        memcpy(PLCresidual, decresidual,this.ULP_inst.blockl*sizeof(float));
                System.Array.Copy(decresidual, 0, PLCresidual, 0, this.ULP_inst.blockl);
                //        memcpy(PLClpc, lpc, (LPC_FILTERORDER+1)*sizeof(float));
                System.Array.Copy(lpc, lpc_idx, PLClpc, 0, ilbc_constants.LPC_FILTERORDER + 1);
                this.consPLICount = 0;
            }

            /* update state */

            if (PLI != 0) {
                this.prevLag = lag;
                this.per = max_per;
            }

            this.prevPLI = PLI;
            //    memcpy(this.prevLpc, PLClpc, (LPC_FILTERORDER+1)*sizeof(float));
            System.Array.Copy(PLClpc, 0, this.prevLpc, 0, ilbc_constants.LPC_FILTERORDER + 1);
            //    memcpy(this.prevResidual, PLCresidual, this.ULP_inst.blockl*sizeof(float));
            System.Array.Copy(PLCresidual, 0, this.prevResidual, 0, this.ULP_inst.blockl);
        }

        //     public int decode(short decoded_data[], short encoded_data[], int mode)
        //     {
        //     return this.ULP_inst.blockl;
        //     }

        public short decode(       /* (o) Number of decoded samples */
           short[] decoded_data,        /* (o) Decoded signal block*/
           short[] encoded_data,        /* (i) Encoded bytes */
           short mode)                       /* (i) 0=PL, 1=Normal */
        {
            int k;
            float[] decblock = new float[ilbc_constants.BLOCKL_MAX];
            float dtmp;
            //       char en_data[] = new char [this.ULP_inst.no_of_bytes];
            bitstream en_data = new bitstream(this.ULP_inst.no_of_bytes);

            /* check if mode is valid */

            if ((mode < 0) || (mode > 1)) {
                //System.Diagnostics.Debug.WriteLine("\nERROR - Wrong mode - 0, 1 allowed\n");
                throw new System.ArgumentException("mode");
            }

            /* do actual decoding of block */
            for (k = 0;k < encoded_data.Length;k++) {
                en_data.buffer[2 * k + 1] = (byte) (encoded_data[k] >> 8);
                en_data.buffer[2 * k] = (byte) (encoded_data[k] & 0xff);
                //        System.out.println("on decode " + (en_data.buffer[2*k]+0) + " et " + (en_data.buffer[2*k+1]+0));
            }

            iLBC_decode(decblock, en_data, mode);

            /* convert to short */
            for (k = 0;k < this.ULP_inst.blockl;k++) {
                dtmp = decblock[k];
                //        System.out.println("on a eu : " + dtmp);

                if (dtmp < ilbc_constants.MIN_SAMPLE)
                    dtmp = ilbc_constants.MIN_SAMPLE;
                else if (dtmp > ilbc_constants.MAX_SAMPLE)
                    dtmp = ilbc_constants.MAX_SAMPLE;
                decoded_data[k] = (short) dtmp;
            }

            return ((short) this.ULP_inst.blockl);
        }

        /*----------------------------------------------------------------*
         *  frame residual decoder function (subrutine to iLBC_decode)
         *---------------------------------------------------------------*/

        public void Decode(
           float[] decresidual,             /* (o) decoded residual frame */
           int start,                      /* (i) location of start
                                              state */
           int idxForMax,                  /* (i) codebook index for the
                                              maximum value */
           int[] idxVec,                /* (i) codebook indexes for the
                                              samples  in the start
                                              state */
           float[] syntdenum,               /* (i) the decoded synthesis
                                              filter coefficients */
           int[] cb_index,                  /* (i) the indexes for the
                                              adaptive codebook */
           int[] gain_index,            /* (i) the indexes for the
                                              corresponding gains */
           int[] extra_cb_index,        /* (i) the indexes for the
                                              adaptive codebook part
                                              of start state */
           int[] extra_gain_index,          /* (i) the indexes for the
                                              corresponding gains */
           int state_first)                 /* (i) 1 if non adaptive part
                                              of start state comes
                                              first 0 if that part
                                              comes last */
        {
            float[] reverseDecresidual = new float[ilbc_constants.BLOCKL_MAX];
            float[] mem = new float[ilbc_constants.CB_MEML];
            int k, meml_gotten, Nfor, Nback, i;
            int diff, start_pos;
            int subcount, subframe;

            diff = ilbc_constants.STATE_LEN - this.ULP_inst.state_short_len;

            if (state_first == 1) {
                start_pos = (start - 1) * ilbc_constants.SUBL;
            } else {
                start_pos = (start - 1) * ilbc_constants.SUBL + diff;
            }

            /* decode scalar part of start state */

            ilbc_common.StateConstructW(idxForMax, idxVec,
                    syntdenum, (start - 1) * (ilbc_constants.LPC_FILTERORDER + 1),
                    decresidual, start_pos, this.ULP_inst.state_short_len);


            if (state_first != 0) { /* put adaptive part in the end */

                /* setup memory */

                for (int li = 0;li < (ilbc_constants.CB_MEML - this.ULP_inst.state_short_len);li++)
                    mem[li] = 0.0f;
                //         memset(mem, 0,
                //            (CB_MEML-this.ULP_inst.state_short_len)*sizeof(float));
                System.Array.Copy(decresidual, start_pos,
                         mem, ilbc_constants.CB_MEML - this.ULP_inst.state_short_len,
                         this.ULP_inst.state_short_len);
                //         memcpy(mem+CB_MEML-this.ULP_inst.state_short_len,
                //            decresidual+start_pos,
                //            this.ULP_inst.state_short_len*sizeof(float));

                /* construct decoded vector */

                ilbc_common.iCBConstruct(decresidual, start_pos + this.ULP_inst.state_short_len,
                             extra_cb_index, 0, extra_gain_index, 0,
                             mem, ilbc_constants.CB_MEML - ilbc_constants.stMemLTbl,
                             ilbc_constants.stMemLTbl, diff, ilbc_constants.CB_NSTAGES);

            } else {/* put adaptive part in the beginning */

                /* create reversed vectors for prediction */

                for (k = 0;k < diff;k++) {
                    reverseDecresidual[k] = decresidual[(start + 1) * ilbc_constants.SUBL - 1 -
                                        (k + this.ULP_inst.state_short_len)];
                }

                /* setup memory */

                meml_gotten = this.ULP_inst.state_short_len;
                for (k = 0;k < meml_gotten;k++) {
                    mem[ilbc_constants.CB_MEML - 1 - k] = decresidual[start_pos + k];
                }
                for (int li = 0;li < ilbc_constants.CB_MEML - k;li++)
                    mem[li] = 0.0f;
                //        memset(mem, 0, (CB_MEML-k)*sizeof(float));

                /* construct decoded vector */

                ilbc_common.iCBConstruct(reverseDecresidual, 0, extra_cb_index, 0,
                             extra_gain_index, 0,
                             mem, ilbc_constants.CB_MEML - ilbc_constants.stMemLTbl,
                             ilbc_constants.stMemLTbl, diff, ilbc_constants.CB_NSTAGES);

                /* get decoded residual from reversed vector */

                for (k = 0;k < diff;k++) {
                    decresidual[start_pos - 1 - k] = reverseDecresidual[k];
                }
            }

            /* counter for predicted sub-frames */

            subcount = 0;

            /* forward prediction of sub-frames */

            Nfor = this.ULP_inst.nsub - start - 1;

            if (Nfor > 0) {

                /* setup memory */

                for (int li = 0;li < ilbc_constants.CB_MEML - ilbc_constants.STATE_LEN;li++)
                    mem[li] = 0.0f;
                //        memset(mem, 0, (CB_MEML-STATE_LEN)*sizeof(float));
                System.Array.Copy(decresidual, (start - 1) * ilbc_constants.SUBL,
                         mem, ilbc_constants.CB_MEML - ilbc_constants.STATE_LEN,
                         ilbc_constants.STATE_LEN);
                //        memcpy(mem+CB_MEML-STATE_LEN, decresidual+(start-1)*SUBL,
                //            STATE_LEN*sizeof(float));

                /* loop over sub-frames to encode */

                for (subframe = 0;subframe < Nfor;subframe++) {

                    /* construct decoded vector */

                    ilbc_common.iCBConstruct(decresidual, (start + 1 + subframe) * ilbc_constants.SUBL,
                                 cb_index, subcount * ilbc_constants.CB_NSTAGES,
                                 gain_index, subcount * ilbc_constants.CB_NSTAGES,
                                 mem, ilbc_constants.CB_MEML - ilbc_constants.memLfTbl[subcount],
                                 ilbc_constants.memLfTbl[subcount], ilbc_constants.SUBL,
                                 ilbc_constants.CB_NSTAGES);

                    /* update memory */

                    System.Array.Copy(mem, ilbc_constants.SUBL,
                             mem, 0,
                             ilbc_constants.CB_MEML - ilbc_constants.SUBL);
                    //        memcpy(mem, mem+SUBL, (CB_MEML-SUBL)*sizeof(float));
                    System.Array.Copy(decresidual, (start + 1 + subframe) * ilbc_constants.SUBL,
                             mem, ilbc_constants.CB_MEML - ilbc_constants.SUBL,
                             ilbc_constants.SUBL);
                    //         memcpy(mem+CB_MEML-SUBL,
                    //                &decresidual[(start+1+subframe)*SUBL],
                    //                SUBL*sizeof(float));

                    subcount++;

                }

            }

            /* backward prediction of sub-frames */

            Nback = start - 1;

            if (Nback > 0) {

                /* setup memory */

                meml_gotten = ilbc_constants.SUBL * (this.ULP_inst.nsub + 1 - start);

                if (meml_gotten > ilbc_constants.CB_MEML) {
                    meml_gotten = ilbc_constants.CB_MEML;
                }
                for (k = 0;k < meml_gotten;k++) {
                    mem[ilbc_constants.CB_MEML - 1 - k] = decresidual[(start - 1) * ilbc_constants.SUBL + k];
                }
                for (int li = 0;li < (ilbc_constants.CB_MEML - k);li++)
                    mem[li] = 0.0f;
                //         memset(mem, 0, (ilbc_constants.CB_MEML-k)*sizeof(float));

                /* loop over subframes to decode */

                for (subframe = 0;subframe < Nback;subframe++) {

                    /* construct decoded vector */

                    ilbc_common.iCBConstruct(reverseDecresidual, subframe * ilbc_constants.SUBL,
                                 cb_index, subcount * ilbc_constants.CB_NSTAGES,
                                 gain_index, subcount * ilbc_constants.CB_NSTAGES,
                                 mem, ilbc_constants.CB_MEML - ilbc_constants.memLfTbl[subcount],
                                 ilbc_constants.memLfTbl[subcount], ilbc_constants.SUBL,
                                 ilbc_constants.CB_NSTAGES);

                    /* update memory */

                    System.Array.Copy(mem, ilbc_constants.SUBL,
                             mem, 0,
                             ilbc_constants.CB_MEML - ilbc_constants.SUBL);
                    //         memcpy(mem, mem+SUBL, (CB_MEML-SUBL)*sizeof(float));
                    System.Array.Copy(reverseDecresidual, subframe * ilbc_constants.SUBL,
                             mem, ilbc_constants.CB_MEML - ilbc_constants.SUBL,
                             ilbc_constants.SUBL);
                    //         memcpy(mem+CB_MEML-SUBL,
                    //                &reverseDecresidual[subframe*SUBL],
                    //                SUBL*sizeof(float));

                    subcount++;
                }

                /* get decoded residual from reversed vector */

                for (i = 0;i < ilbc_constants.SUBL * Nback;i++)
                    decresidual[ilbc_constants.SUBL * Nback - i - 1] =
                        reverseDecresidual[i];
            }
        }


        /*----------------------------------------------------------------*
         *  main decoder function
         *---------------------------------------------------------------*/

        void iLBC_decode(
                 float[] decblock,            /* (o) decoded signal block */
                 bitstream bytes,           /* (i) encoded signal bits */
                 int mode)                   /* (i) 0: bad packet, PLC,
                            1: normal */
        {
            float[] data = new float[ilbc_constants.BLOCKL_MAX];
            float[] lsfdeq = new float[ilbc_constants.LPC_FILTERORDER * ilbc_constants.LPC_N_MAX];
            float[] PLCresidual = new float[ilbc_constants.BLOCKL_MAX];
            float[] PLClpc = new float[ilbc_constants.LPC_FILTERORDER + 1];
            float[] zeros = new float[ilbc_constants.BLOCKL_MAX];
            float[] one = new float[ilbc_constants.LPC_FILTERORDER + 1];
            int k, i, start, idxForMax, /*pos,*/ lastpart, ulp;
            int lag, ilag;
            float cc, maxcc;
            int[] idxVec = new int[ilbc_constants.STATE_LEN];
            //    int check;
            int[] gain_index = new int[ilbc_constants.NASUB_MAX * ilbc_constants.CB_NSTAGES];
            int[] extra_gain_index = new int[ilbc_constants.CB_NSTAGES];
            int[] cb_index = new int[ilbc_constants.CB_NSTAGES * ilbc_constants.NASUB_MAX];
            int[] extra_cb_index = new int[ilbc_constants.CB_NSTAGES];
            int[] lsf_i = new int[ilbc_constants.LSF_NSPLIT * ilbc_constants.LPC_N_MAX];
            int state_first;
            int last_bit;
            //    unsigned char *pbytes;
            float[] weightdenum = new float[(ilbc_constants.LPC_FILTERORDER + 1) *
                               ilbc_constants.NSUB_MAX];
            int order_plus_one;
            float[] syntdenum = new float[ilbc_constants.NSUB_MAX * (ilbc_constants.LPC_FILTERORDER + 1)];
            float[] decresidual = new float[ilbc_constants.BLOCKL_MAX];

            if (mode > 0) { /* the data are good */

                /* decode data */

                //        pbytes=bytes;
                // pos=0;

                /* Set everything to zero before decoding */

                for (k = 0;k < ilbc_constants.LSF_NSPLIT * ilbc_constants.LPC_N_MAX;k++) {
                    lsf_i[k] = 0;
                }
                start = 0;
                state_first = 0;
                idxForMax = 0;
                for (k = 0;k < this.ULP_inst.state_short_len;k++) {
                    idxVec[k] = 0;
                }
                for (k = 0;k < ilbc_constants.CB_NSTAGES;k++) {
                    extra_cb_index[k] = 0;
                }
                for (k = 0;k < ilbc_constants.CB_NSTAGES;k++) {
                    extra_gain_index[k] = 0;
                }
                for (i = 0;i < this.ULP_inst.nasub;i++) {
                    for (k = 0;k < ilbc_constants.CB_NSTAGES;k++) {
                        cb_index[i * ilbc_constants.CB_NSTAGES + k] = 0;
                    }
                }
                for (i = 0;i < this.ULP_inst.nasub;i++) {
                    for (k = 0;k < ilbc_constants.CB_NSTAGES;k++) {
                        gain_index[i * ilbc_constants.CB_NSTAGES + k] = 0;
                    }
                }

                /* loop over ULP classes */

                for (ulp = 0;ulp < 3;ulp++) {

                    /* LSF */
                    for (k = 0;k < ilbc_constants.LSF_NSPLIT * this.ULP_inst.lpc_n;k++) {
                        lastpart = bytes.unpack(this.ULP_inst.lsf_bits[k, ulp]);
                        //             unpack( &pbytes, &lastpart,
                        //                 this.ULP_inst.lsf_bits[k,ulp], &pos);
                        lsf_i[k] = bytes.packcombine(lsf_i[k], lastpart,
                                     this.ULP_inst.lsf_bits[k, ulp]);
                        //            System.out.println("lsf_i["+k+"] = " + lsf_i[k]);
                        //             packcombine(&lsf_i[k], lastpart,
                        //                 this.ULP_inst.lsf_bits[k,ulp]);
                    }

                    /* Start block info */

                    lastpart = bytes.unpack(this.ULP_inst.start_bits[ulp]);
                    //         unpack( &pbytes, &lastpart,
                    //             this.ULP_inst.start_bits[ulp], &pos);
                    start = bytes.packcombine(start, lastpart,
                                this.ULP_inst.start_bits[ulp]);
                    //        System.out.println("start = " + start);
                    //         packcombine(&start, lastpart,
                    //                 this.ULP_inst.start_bits[ulp]);

                    lastpart = bytes.unpack(this.ULP_inst.startfirst_bits[ulp]);
                    //         unpack( &pbytes, &lastpart,
                    //             this.ULP_inst.startfirst_bits[ulp], &pos);
                    state_first = bytes.packcombine(state_first, lastpart,
                                    this.ULP_inst.startfirst_bits[ulp]);
                    //        System.out.println("state_first = " + state_first);
                    //         packcombine(&state_first, lastpart,
                    //                 this.ULP_inst.startfirst_bits[ulp]);

                    lastpart = bytes.unpack(this.ULP_inst.scale_bits[ulp]);
                    //         unpack( &pbytes, &lastpart,
                    //             this.ULP_inst.scale_bits[ulp], &pos);
                    idxForMax = bytes.packcombine(idxForMax, lastpart,
                                      this.ULP_inst.scale_bits[ulp]);
                    //        System.out.println("idxForMax = " + idxForMax);
                    //         packcombine(&idxForMax, lastpart,
                    //                 this.ULP_inst.scale_bits[ulp]);

                    for (k = 0;k < this.ULP_inst.state_short_len;k++) {
                        lastpart = bytes.unpack(this.ULP_inst.state_bits[ulp]);
                        //             unpack( &pbytes, &lastpart,
                        //                 this.ULP_inst.state_bits[ulp], &pos);
                        idxVec[k] = bytes.packcombine(idxVec[k], lastpart,
                                      this.ULP_inst.state_bits[ulp]);
                        //            System.out.println("idxVec["+k+"] = " + idxVec[k]);
                        //             packcombine(idxVec+k, lastpart,
                        //                 this.ULP_inst.state_bits[ulp]);
                    }

                    /* 23/22 (20ms/30ms) sample block */

                    for (k = 0;k < ilbc_constants.CB_NSTAGES;k++) {
                        lastpart = bytes.unpack(this.ULP_inst.extra_cb_index[k, ulp]);
                        //             unpack( &pbytes, &lastpart,
                        //                 this.ULP_inst.extra_cb_index[k,ulp],
                        //                 &pos);
                        extra_cb_index[k] = bytes.packcombine(extra_cb_index[k], lastpart,
                                          this.ULP_inst.extra_cb_index[k, ulp]);
                        //            System.out.println("extra_cb_index["+k+"] = " + extra_cb_index[k]);
                        //             packcombine(extra_cb_index+k, lastpart,
                        //                 this.ULP_inst.extra_cb_index[k,ulp]);
                    }
                    for (k = 0;k < ilbc_constants.CB_NSTAGES;k++) {
                        lastpart = bytes.unpack(this.ULP_inst.extra_cb_gain[k, ulp]);
                        //             unpack( &pbytes, &lastpart,
                        //                 this.ULP_inst.extra_cb_gain[k,ulp],
                        //                 &pos);
                        extra_gain_index[k] = bytes.packcombine(extra_gain_index[k], lastpart,
                                            this.ULP_inst.extra_cb_gain[k, ulp]);
                        //            System.out.println("extra_gain_index["+k+"] = " + extra_gain_index[k]);
                        //             packcombine(extra_gain_index+k, lastpart,
                        //                 this.ULP_inst.extra_cb_gain[k,ulp]);
                    }

                    /* The two/four (20ms/30ms) 40 sample sub-blocks */

                    for (i = 0;i < this.ULP_inst.nasub;i++) {
                        for (k = 0;k < ilbc_constants.CB_NSTAGES;k++) {
                            lastpart = bytes.unpack(this.ULP_inst.cb_index[i, k, ulp]);
                            //             unpack( &pbytes, &lastpart,
                            //                 this.ULP_inst.cb_index[i,k,ulp],
                            //                 &pos);
                            cb_index[i * ilbc_constants.CB_NSTAGES + k] =
                                bytes.packcombine(cb_index[i * ilbc_constants.CB_NSTAGES + k], lastpart,
                                          this.ULP_inst.cb_index[i, k, ulp]);
                            //            System.out.println("cb_index["+(i*ilbc_constants.CB_NSTAGES+k)+"] = " + cb_index[(i*ilbc_constants.CB_NSTAGES+k)]);
                            //             packcombine(cb_index+i*CB_NSTAGES+k, lastpart,
                            //                     this.ULP_inst.cb_index[i,k,ulp]);
                        }
                    }

                    for (i = 0;i < this.ULP_inst.nasub;i++) {
                        for (k = 0;k < ilbc_constants.CB_NSTAGES;k++) {
                            lastpart = bytes.unpack(this.ULP_inst.cb_gain[i, k, ulp]);
                            gain_index[i * ilbc_constants.CB_NSTAGES + k] =
                                bytes.packcombine(gain_index[i * ilbc_constants.CB_NSTAGES + k], lastpart,
                                          this.ULP_inst.cb_gain[i, k, ulp]);
                            //            System.out.println("gain_index["+(i*ilbc_constants.CB_NSTAGES+k)+"] = " + gain_index[(i*ilbc_constants.CB_NSTAGES+k)]);
                        }
                    }
                }
                /* Extract last bit. If it is 1 this indicates an
                   empty/lost frame */
                last_bit = bytes.unpack(1);
                //        System.out.println("last_bit = "  + last_bit);

                /* Check for bit errors or empty/lost frames */
                if (start < 1)
                    mode = 0;
                if (this.ULP_inst.mode == 20 && start > 3)
                    mode = 0;
                if (this.ULP_inst.mode == 30 && start > 5)
                    mode = 0;
                if (last_bit == 1)
                    mode = 0;

                if (mode == 1) { /* No bit errors was detected,
                  continue decoding */

                    /* adjust index */
                    index_conv_dec(cb_index);

                    //         for (int li = 0; li < cb_index.Length; li++)
                    //             System.out.println("cb_index["+li+"] = " + cb_index[li]);

                    /* decode the lsf */

                    SimplelsfDEQ(lsfdeq, lsf_i, this.ULP_inst.lpc_n);
                    //         for (int li = 0; li < lsfdeq.Length; li++)
                    //             System.out.println("lsfdeq["+li+"] = " + lsfdeq[li]);
                    ilbc_common.LSF_check(lsfdeq, ilbc_constants.LPC_FILTERORDER,
                            this.ULP_inst.lpc_n);
                    //        check=ilbc_common.LSF_check(lsfdeq, ilbc_constants.LPC_FILTERORDER,
                    //                this.ULP_inst.lpc_n);
                    //         System.out.println("check returns " + check);
                    DecoderInterpolateLSF(syntdenum, weightdenum,
                                  lsfdeq, ilbc_constants.LPC_FILTERORDER);
                    //         for (int li = 0; li < syntdenum.Length; li++)
                    //             System.out.println("syntdenum[" + li + "] = " + syntdenum[li]);
                    //         for (int li = 0; li < weightdenum.Length; li++)
                    //             System.out.println("weightdenum[" + li + "] = " + weightdenum[li]);

                    Decode(decresidual, start, idxForMax,
                           idxVec, syntdenum, cb_index, gain_index,
                           extra_cb_index, extra_gain_index,
                           state_first);

                    //         for (int li = 0; li < decresidual.Length; li++)
                    //             System.out.println("decresidual[" + li + "] = " + decresidual[li]);

                    /* preparing the plc for a future loss! */

                    doThePLC(PLCresidual, PLClpc, 0, decresidual,
                         syntdenum,
                         (ilbc_constants.LPC_FILTERORDER + 1) * (this.ULP_inst.nsub - 1),
                         last_lag);

                    System.Array.Copy(PLCresidual, 0, decresidual, 0, this.ULP_inst.blockl);
                    //         for (int li = 0; li < decresidual.Length; li++)
                    //             System.out.println("decresidual[" + li + "] = " + decresidual[li]);
                    //         memcpy(decresidual, PLCresidual,
                    //                this.ULP_inst.blockl*sizeof(float));
                }

            }

            if (mode == 0) {
                /* the data is bad (either a PLC call
                 * was made or a severe bit error was detected)
                 */

                /* packet loss conceal */

                for (int li = 0;li < ilbc_constants.BLOCKL_MAX;li++)
                    zeros[li] = 0.0f;
                //        memset(zeros, 0, BLOCKL_MAX*sizeof(float));

                one[0] = 1;
                for (int li = 0;li < ilbc_constants.LPC_FILTERORDER;li++)
                    one[li + 1] = 0.0f;
                //        memset(one+1, 0, LPC_FILTERORDER*sizeof(float));

                start = 0;

                doThePLC(PLCresidual, PLClpc, 1, zeros, one, 0,
                     last_lag);
                System.Array.Copy(PLCresidual, 0, decresidual, 0, this.ULP_inst.blockl);
                //         memcpy(decresidual, PLCresidual,
                //            this.ULP_inst.blockl*sizeof(float));

                order_plus_one = ilbc_constants.LPC_FILTERORDER + 1;
                for (i = 0;i < this.ULP_inst.nsub;i++) {
                    System.Array.Copy(PLClpc, 0, syntdenum, (i * order_plus_one), order_plus_one);
                    //         memcpy(syntdenum+(i*order_plus_one), PLClpc,
                    //                order_plus_one*sizeof(float));
                }
            }

            if (this.use_enhancer == 1) {

                /* post filtering */

                this.last_lag = enhancerInterface(data, decresidual);

                //         System.out.println("last_lag : " + this.last_lag);

                //        for (int li = 0; li < data.Length; li++)
                //          System.out.println("data["+li+"] = " + data[li]);

                //        for (li = 0; li <

                /* synthesis filtering */

                if (this.ULP_inst.mode == 20) {
                    /* Enhancer has 40 samples delay */
                    i = 0;
                    //         System.out.println("run 1");
                    syntFilter(data, i * ilbc_constants.SUBL,
                           this.old_syntdenum,
                           (i + this.ULP_inst.nsub - 1) * (ilbc_constants.LPC_FILTERORDER + 1),
                           ilbc_constants.SUBL, this.syntMem);
                    //         System.out.println("runs 2");
                    for (i = 1;i < this.ULP_inst.nsub;i++) {
                        //             System.out.println("pass " + i);
                        syntFilter(data, i * ilbc_constants.SUBL,
                               syntdenum, (i - 1) * (ilbc_constants.LPC_FILTERORDER + 1),
                               ilbc_constants.SUBL, this.syntMem);
                        //             System.out.println("pass " + i + " ends");
                    }
                    //        for (int li = 0; li < data.Length; li++)
                    //          System.out.println("psdata["+li+"] = " + data[li]);

                } else if (this.ULP_inst.mode == 30) {
                    /* Enhancer has 80 samples delay */
                    //         System.out.println("runs 3");
                    for (i = 0;i < 2;i++) {
                        syntFilter(data, i * ilbc_constants.SUBL,
                               this.old_syntdenum,
                               (i + this.ULP_inst.nsub - 2) * (ilbc_constants.LPC_FILTERORDER + 1),
                               ilbc_constants.SUBL, this.syntMem);
                    }
                    for (i = 2;i < this.ULP_inst.nsub;i++) {
                        //             System.out.println("runs 4");
                        syntFilter(data, i * ilbc_constants.SUBL,
                               syntdenum, (i - 2) * (ilbc_constants.LPC_FILTERORDER + 1),
                               ilbc_constants.SUBL, this.syntMem);
                    }
                }

            } else {

                /* Find last lag */
                lag = 20;
                maxcc = xCorrCoef(decresidual,
                          ilbc_constants.BLOCKL_MAX - ilbc_constants.ENH_BLOCKL,
                          decresidual,
                          ilbc_constants.BLOCKL_MAX - ilbc_constants.ENH_BLOCKL - lag,
                          ilbc_constants.ENH_BLOCKL);

                for (ilag = 21;ilag < 120;ilag++) {
                    cc = xCorrCoef(decresidual,
                               ilbc_constants.BLOCKL_MAX - ilbc_constants.ENH_BLOCKL,
                               decresidual,
                               ilbc_constants.BLOCKL_MAX - ilbc_constants.ENH_BLOCKL - ilag,
                               ilbc_constants.ENH_BLOCKL);

                    if (cc > maxcc) {
                        maxcc = cc;
                        lag = ilag;
                    }
                }
                this.last_lag = lag;

                /* copy data and run synthesis filter */

                System.Array.Copy(decresidual, 0, data, 0, this.ULP_inst.blockl);
                //         memcpy(data, decresidual,
                //            this.ULP_inst.blockl*sizeof(float));
                //         System.out.println("runs 5");
                for (i = 0;i < this.ULP_inst.nsub;i++) {
                    syntFilter(data, i * ilbc_constants.SUBL,
                           syntdenum, i * (ilbc_constants.LPC_FILTERORDER + 1),
                           ilbc_constants.SUBL, this.syntMem);
                }

            }

            /* high pass filtering on output if desired, otherwise
               copy to out */

            hpOutput(data, this.ULP_inst.blockl, decblock, this.hpomem);

            /* memcpy(decblock,data,iLBCdec_inst->blockl*sizeof(float));*/

            System.Array.Copy(syntdenum, 0, this.old_syntdenum, 0,
                     this.ULP_inst.nsub * (ilbc_constants.LPC_FILTERORDER + 1));
            //     memcpy(this.old_syntdenum, syntdenum,
            //            this.ULP_inst.nsub*(LPC_FILTERORDER+1)*sizeof(float));

            this.prev_enh_pl = 0;

            if (mode == 0) { /* PLC was used */
                this.prev_enh_pl = 1;
            }
        }


        public ilbc_decoder(int init_mode, int init_enhancer) {
            ULP_inst = new ilbc_ulp(init_mode);
            /* properties to initialize : */
            syntMem = new float[ilbc_constants.LPC_FILTERORDER];
            prevLpc = new float[ilbc_constants.LPC_FILTERORDER + 1];
            prevResidual = new float[ilbc_constants.NSUB_MAX * ilbc_constants.SUBL];
            old_syntdenum = new float[(ilbc_constants.LPC_FILTERORDER + 1) * ilbc_constants.NSUB_MAX];
            hpomem = new float[4];
            enh_buf = new float[ilbc_constants.ENH_BUFL];
            enh_period = new float[ilbc_constants.ENH_NBLOCKS_TOT];
            lsfdeqold = new float[ilbc_constants.LPC_FILTERORDER];

            for (int li = 0;li < syntMem.Length;li++)
                syntMem[li] = 0.0f;

            System.Array.Copy(ilbc_constants.lsfmeanTbl, 0, lsfdeqold, 0,
                     ilbc_constants.LPC_FILTERORDER);
            //     for (int li = 0; li < lsfdeqold.Length; li++)
            //         lsfdeqold[li] = 0.0f;

            for (int li = 0;li < old_syntdenum.Length;li++)
                old_syntdenum[li] = 0.0f;

            for (int li = 0;li < ilbc_constants.NSUB_MAX;li++)
                old_syntdenum[li * (ilbc_constants.LPC_FILTERORDER + 1)] = 1.0f;

            last_lag = 20;
            prevLag = 120;
            per = 0.0f;
            consPLICount = 0;
            prevPLI = 0;
            prevLpc[0] = 1.0f;
            for (int li = 1;li < prevLpc.Length;li++)
                prevLpc[li] = 0.0f;
            for (int li = 0;li < prevResidual.Length;li++)
                prevResidual[li] = 0.0f;
            seed = 777;

            for (int li = 0;li < hpomem.Length;li++)
                hpomem[li] = 0.0f;

            use_enhancer = init_enhancer;
            for (int li = 0;li < enh_buf.Length;li++)
                enh_buf[li] = 0.0f;
            for (int li = 0;li < ilbc_constants.ENH_NBLOCKS_TOT;li++)
                enh_period[li] = 40.0f;
            prev_enh_pl = 0;
        }
    }

}